package org.example;

import java.io.File;
import java.sql.SQLException;
import java.util.*;

public class VocabEstimator {
    private static final String[] LEVELS = {"A", "B", "C", "D", "E", "F", "G", "H"};

    public static final Map<String, String> LEVEL_NAME_MAP = new HashMap<>();
    static {
        LEVEL_NAME_MAP.put("A", "母语级");
        LEVEL_NAME_MAP.put("B", "精通级");
        LEVEL_NAME_MAP.put("C", "高级");
        LEVEL_NAME_MAP.put("D", "进阶中高级");
        LEVEL_NAME_MAP.put("E", "中高级");
        LEVEL_NAME_MAP.put("F", "中级");
        LEVEL_NAME_MAP.put("G", "初级");
        LEVEL_NAME_MAP.put("H", "基础");
    }

    public static EstimateResult estimate(File file) throws Exception {
        Set<String> words = TextUtil.extractWords(file);
        System.out.println("提取到的单词：" + words);
        Map<String, Integer> levelHits = new LinkedHashMap<>();
        int[] totalWords = new int[LEVELS.length];
        int[] hits = new int[LEVELS.length];
        // 1. 动态获取每个等级表总单词数和命中数
        for (int i = 0; i < LEVELS.length; i++) {
            String level = LEVELS[i];
            Set<String> hitSet = OracleUtil.findWordsInLevel(level, words);
            hits[i] = hitSet.size();
            totalWords[i] = OracleUtil.countWordsInLevel(level);
            levelHits.put(level, hits[i]);
        }
        // 2. 计算各等级"掌握度"
        double[] masteryRatios = new double[LEVELS.length];
        for (int i = 0; i < LEVELS.length; i++) {
            masteryRatios[i] = totalWords[i] == 0 ? 0 : (double)hits[i] / totalWords[i];
        }
        // 3. 计算用户的"能力分数"
        double[] difficultyScores = {128, 64, 32, 16, 8, 4, 2, 1}; // 2的幂次方，指数级难度权重
        double numerator = 0;
        double denominator = 0;
        for (int i = 0; i < LEVELS.length; i++) {
            numerator += masteryRatios[i] * difficultyScores[i];
            denominator += masteryRatios[i];
        }
        double abilityScoreRaw = (denominator == 0) ? 1.0 : numerator / denominator;
        // 对数变换，将分数拉伸到更合理的范围
        double abilityScore = (abilityScoreRaw <= 1.0) ? 1.0 : Math.log(abilityScoreRaw) / Math.log(2);
        // 4. 映射能力分数到最终等级
        String userLevel = mapAbilityScoreToLevel(abilityScore);
        // 5. 估算词汇量（基于能力分数的线性插值）
        int estimatedVocab = estimateVocabByAbility(abilityScore);
        // 6. 可信度
        int totalHit = 0;
        for(int hit : hits) totalHit += hit;
        double confidence;
        if (totalHit < 20) confidence = 0.3;
        else if (totalHit < 100) confidence = 0.5;
        else if (totalHit < 300) confidence = 0.7;
        else confidence = 0.9;
        return new EstimateResult(userLevel, estimatedVocab, confidence, levelHits, 0, 0);
    }

    private static String mapAbilityScoreToLevel(double score) {
//        if (score >= 7.5) return "A";
//        if (score >= 6.5) return "B";
//        if (score >= 5.5) return "C";
//        if (score >= 4.5) return "D";
//        if (score >= 3.5) return "E";
//        if (score >= 2.5) return "F";
//        if (score >= 1.5) return "G";
//        return "H";
        if (score >= 7) return "A";
        if (score >= 6) return "B";
        if (score >= 5) return "C";
        if (score >= 4) return "D";
        if (score >= 3) return "E";
        if (score >= 2) return "F";
        if (score >= 1) return "G";
        return "H";

    }

    private static int estimateVocabByAbility(double score) {
        // 基准：分数1~8对应的词汇量
        int[] vocabBenchmarks = {500, 1500, 3000, 5000, 8000, 11000, 15000, 20000};
        if (score <= 1) return vocabBenchmarks[0];
        if (score >= 8) return vocabBenchmarks[7];
        int lowerIdx = (int)Math.floor(score) - 1;
        int upperIdx = (int)Math.ceil(score) - 1;
        double lowerVocab = vocabBenchmarks[lowerIdx];
        double upperVocab = vocabBenchmarks[upperIdx];
        // 线性插值
        return (int)(lowerVocab + (score - Math.floor(score)) * (upperVocab - lowerVocab));
    }

    private static String determineLevel(Map<String, Integer> levelHits) {
        for (String level : LEVELS) {
            if (levelHits.get(level) > 0) return level;
        }
        return "H";
    }

    private static double calcConfidence(int totalWords, int hitWords) {
        if (totalWords == 0) return 0.0;
        return (double) hitWords / totalWords;
    }
}